1use crate::copp::clarabel_backend::{ConstraintsClarabel, ObjConsClarabel};
31use crate::copp::copp3::Topp3Profile;
32use crate::copp::copp3::Topp3ProfileRef;
33use crate::copp::copp3::formulation::{Copp3Problem, get_weight_a_copp3, get_weight_a_topp3};
34use crate::copp::copp3::opt3::ClarabelExpertInfor3rd;
35use crate::copp::copp3::opt3::clarabel_constraints::{
36 clarabel_standard_capacity_topp3, clarabel_standard_constraint_topp3,
37};
38use crate::copp::{
39 ClarabelOptions, CoppObjective, clarabel_to_copp3_solution, validate_copp3_objectives,
40};
41use crate::diag::{
42 CoppError, DebugVerboser, SilentVerboser, SummaryVerboser, TraceVerboser, Verboser, Verbosity,
43 check_boundary_state_copp3_valid, check_s_interval_valid, format_duration_human,
44};
45use crate::robot::robot_core::{Robot, RobotBasic, RobotTorque};
46use clarabel::algebra::CscMatrix;
47use clarabel::solver::SupportedConeT::{NonnegativeConeT, SecondOrderConeT};
48use clarabel::solver::{DefaultSolution, DefaultSolver, IPSolver, SupportedConeT};
49use core::f64;
50use itertools::{Itertools, izip};
51use nalgebra::{DMatrix, DVectorView};
52
53pub fn copp3_socp<'a, M: RobotTorque>(
74 problem: &Copp3Problem<'a, M>,
75 options: &ClarabelOptions,
76) -> Result<Topp3Profile, CoppError> {
77 let (result, solution) = copp3_socp_expert(problem, options)?;
78 result.ok_or_else(|| CoppError::ClarabelSolverStatus("copp3_socp".into(), solution.status))
79}
80
81pub fn copp3_socp_expert<'a, M: RobotTorque>(
105 problem: &Copp3Problem<'a, M>,
106 options: &ClarabelOptions,
107) -> Result<(Option<Topp3Profile>, DefaultSolution<f64>), CoppError> {
108 let info = copp3_socp_expert_with_info(problem, options)?;
109 let _ = &info.linsolver;
110 Ok((info.result, info.solution))
111}
112
113pub fn copp3_socp_expert_with_info<'a, M: RobotTorque>(
119 problem: &Copp3Problem<'a, M>,
120 options: &ClarabelOptions,
121) -> Result<ClarabelExpertInfor3rd, CoppError> {
122 match options.verbosity() {
123 Verbosity::Silent => copp3_socp_core(problem, (options, SilentVerboser)),
124 Verbosity::Summary => copp3_socp_core(problem, (options, SummaryVerboser::new())),
125 Verbosity::Debug => copp3_socp_core(problem, (options, DebugVerboser::new())),
126 Verbosity::Trace => copp3_socp_core(problem, (options, TraceVerboser::new())),
127 }
128}
129
130fn copp3_socp_core<'a, M: RobotTorque>(
142 problem: &Copp3Problem<'a, M>,
143 options_verboser: (&ClarabelOptions, impl Verboser),
144) -> Result<ClarabelExpertInfor3rd, CoppError> {
145 let (options, mut verboser) = options_verboser;
146 let idx_s_start = problem.idx_s_start;
147 let a_boundary = problem.a_boundary;
148 let b_boundary = problem.b_boundary;
149 let num_stationary = problem.num_stationary;
150 if verboser.is_enabled(Verbosity::Summary) {
151 verboser.record_start_time();
152 }
153 if verboser.is_enabled(Verbosity::Trace) {
154 let settings = options.clarabel_settings();
155 crate::verbosity_log!(
156 crate::diag::Verbosity::Summary,
157 "copp3_socp: options snapshot -> allow(almost={}, max_iter={}, max_time={}, callback_term={}, insufficient_progress={}), tol_gap_rel={}, tol_feas={}, max_iter={}, verbose={}",
158 options.is_allow(clarabel::solver::SolverStatus::AlmostSolved),
159 options.is_allow(clarabel::solver::SolverStatus::MaxIterations),
160 options.is_allow(clarabel::solver::SolverStatus::MaxTime),
161 options.is_allow(clarabel::solver::SolverStatus::CallbackTerminated),
162 options.is_allow(clarabel::solver::SolverStatus::InsufficientProgress),
163 settings.tol_gap_rel,
164 settings.tol_feas,
165 settings.max_iter,
166 settings.verbose
167 );
168 }
169 check_boundary_state_copp3_valid(a_boundary, b_boundary)?;
171 let n = problem.a_linearization.len() - 1;
172 let idx_s_final = idx_s_start + n;
173 if verboser.is_enabled(Verbosity::Summary) {
174 crate::verbosity_log!(
175 crate::diag::Verbosity::Summary,
176 "\ncopp3_socp started: {} <= idx_s <= {}, objectives = {}, s_len = {}.",
177 idx_s_start,
178 idx_s_final,
179 problem.objectives.len(),
180 problem.a_linearization.len()
181 );
182 }
183 check_s_interval_valid("copp3_socp", idx_s_start, idx_s_final)?;
184 validate_copp3_objectives(
185 "copp3_socp",
186 problem.objectives,
187 problem.robot.dim(),
188 problem.a_linearization.len(),
189 )?;
190 let (cap_val_std, cap_b_std, cap_cone_std) =
197 clarabel_standard_capacity_topp3(&problem.robot.constraints, (idx_s_start, idx_s_final));
198 let (cap_val_obj, cap_b_obj, cap_cone_obj, n_vars) =
199 clarabel_objective_capacity_copp3(n, problem.objectives, problem.robot);
200 if verboser.is_enabled(Verbosity::Debug) {
201 crate::verbosity_log!(
202 crate::diag::Verbosity::Summary,
203 "copp3_socp: capacity estimate std(val={cap_val_std}, b={cap_b_std}, cone={cap_cone_std}), obj(val={cap_val_obj}, b={cap_b_obj}, cone={cap_cone_obj}), n_vars={n_vars}."
204 );
205 }
206 let mut row = Vec::<usize>::with_capacity(cap_val_std + cap_val_obj);
209 let mut col = Vec::<usize>::with_capacity(cap_val_std + cap_val_obj);
210 let mut val = Vec::<f64>::with_capacity(cap_val_std + cap_val_obj);
211 let mut b = Vec::<f64>::with_capacity(cap_b_std + cap_b_obj);
212 let mut cones = Vec::<SupportedConeT<f64>>::with_capacity(cap_cone_std + cap_cone_obj);
213 if verboser.is_enabled(Verbosity::Trace) {
214 crate::verbosity_log!(
215 crate::diag::Verbosity::Summary,
216 "copp3_socp: allocated capacities row/col/val/b/cones <= {}/{}/{}/{}/{}",
217 cap_val_std + cap_val_obj,
218 cap_val_std + cap_val_obj,
219 cap_val_std + cap_val_obj,
220 cap_b_std + cap_b_obj,
221 cap_cone_std + cap_cone_obj
222 );
223 }
224 let s = problem
226 .robot
227 .constraints
228 .s_vec(idx_s_start, idx_s_final + 1)?;
229 let row_before_std = row.len();
230 let col_before_std = col.len();
231 let val_before_std = val.len();
232 let b_before_std = b.len();
233 let cones_before_std = cones.len();
234 clarabel_standard_constraint_topp3(
235 &problem.as_topp3_problem(),
236 &s,
237 (&mut row, &mut col, &mut val, &mut b, &mut cones),
238 num_stationary,
239 &verboser,
240 )?;
241 if verboser.is_enabled(Verbosity::Trace) {
242 crate::verbosity_log!(
243 crate::diag::Verbosity::Summary,
244 "copp3_socp: standard-constraints delta row/col/val/b/cones = +{}/+{}/+{}/+{}/+{}",
245 row.len() - row_before_std,
246 col.len() - col_before_std,
247 val.len() - val_before_std,
248 b.len() - b_before_std,
249 cones.len() - cones_before_std
250 );
251 }
252 let row_before_sqrt = row.len();
255 let col_before_sqrt = col.len();
256 let val_before_sqrt = val.len();
257 let b_before_sqrt = b.len();
258 let cones_before_sqrt = cones.len();
259 let n_var_old = clarabel_sqrt_a_copp3(
260 n,
261 problem.objectives,
262 (&mut row, &mut col, &mut val, &mut b, &mut cones),
263 num_stationary,
264 );
265 if verboser.is_enabled(Verbosity::Trace) {
266 crate::verbosity_log!(
267 crate::diag::Verbosity::Summary,
268 "copp3_socp: sqrt-a stage delta row/col/val/b/cones = +{}/+{}/+{}/+{}/+{}, n_var_old={}",
269 row.len() - row_before_sqrt,
270 col.len() - col_before_sqrt,
271 val.len() - val_before_sqrt,
272 b.len() - b_before_sqrt,
273 cones.len() - cones_before_sqrt,
274 n_var_old
275 );
276 }
277 let mut q_object = Vec::<f64>::with_capacity(n_vars);
278 q_object.resize(n_var_old, 0.0);
279 let row_before_obj = row.len();
281 let col_before_obj = col.len();
282 let val_before_obj = val.len();
283 let b_before_obj = b.len();
284 let cones_before_obj = cones.len();
285 let q_before_obj = q_object.len();
286 clarabel_objective_copp3(
287 problem,
288 num_stationary,
289 (
290 &mut row,
291 &mut col,
292 &mut val,
293 &mut b,
294 &mut cones,
295 &mut q_object,
296 ),
297 )?;
298 if verboser.is_enabled(Verbosity::Trace) {
299 let (q_min, q_max) = q_object
300 .iter()
301 .fold((f64::INFINITY, f64::NEG_INFINITY), |(mn, mx), &v| {
302 (mn.min(v), mx.max(v))
303 });
304 crate::verbosity_log!(
305 crate::diag::Verbosity::Summary,
306 "copp3_socp: objective stage delta row/col/val/b/cones/q = +{}/+{}/+{}/+{}/+{}/+{}, q_range=[{}, {}]",
307 row.len() - row_before_obj,
308 col.len() - col_before_obj,
309 val.len() - val_before_obj,
310 b.len() - b_before_obj,
311 cones.len() - cones_before_obj,
312 q_object.len() - q_before_obj,
313 q_min,
314 q_max
315 );
316 }
317 if verboser.is_enabled(Verbosity::Debug) {
318 crate::verbosity_log!(
319 crate::diag::Verbosity::Summary,
320 "copp3_socp: after objective assembly row={}, col={}, val={}, b={}, cones={}, q={}",
321 row.len(),
322 col.len(),
323 val.len(),
324 b.len(),
325 cones.len(),
326 q_object.len()
327 );
328 }
329 let n_var = q_object.len();
331 let row_len = row.len();
332 let col_len = col.len();
333 let val_len = val.len();
334 let b_len = b.len();
335 let cones_len = cones.len();
336 let a_csc = CscMatrix::new_from_triplets(b.len(), n_var, row, col, val);
337 let p_object = CscMatrix::<f64>::zeros((n_var, n_var));
338 if verboser.is_enabled(Verbosity::Trace) {
339 crate::verbosity_log!(
340 crate::diag::Verbosity::Summary,
341 "copp3_socp: matrix built with m={}, n={}, A.nnz={}, P.nnz={}",
342 b.len(),
343 n_var,
344 a_csc.nnz(),
345 p_object.nnz()
346 );
347 }
348 if verboser.is_enabled(Verbosity::Summary) {
349 crate::verbosity_log!(
350 crate::diag::Verbosity::Summary,
351 "copp3_socp: ready to solve with row/col/val/b/cones = {row_len}/{col_len}/{val_len}/{b_len}/{cones_len} and n_var = {n_var}.",
352 );
353 }
354 let settings = options.clarabel_settings().clone();
356 let mut solver = DefaultSolver::<f64>::new(&p_object, &q_object, &a_csc, &b, &cones, settings)
357 .map_err(|e| CoppError::ClarabelSolverError("copp3_socp".into(), e))?;
358 solver.solve();
359 let linsolver = solver.info.linsolver.clone();
360 let solution = solver.solution;
361 if verboser.is_enabled(Verbosity::Summary) {
362 crate::verbosity_log!(
363 crate::diag::Verbosity::Summary,
364 "copp3_socp: solve done, status = {:?}, elapsed = {}.",
365 solution.status,
366 format_duration_human(verboser.elapsed())
367 );
368 }
369 if verboser.is_enabled(Verbosity::Trace) {
370 let show = solution.x.len().min(3);
371 crate::verbosity_log!(
372 crate::diag::Verbosity::Summary,
373 "copp3_socp: solution x_len={}, head={:?}",
374 solution.x.len(),
375 &solution.x[0..show]
376 );
377 }
378 let result = if options.is_allow(solution.status) {
379 Some(clarabel_to_copp3_solution(
380 &solution.x.as_slice()[0..2 * (n + 1)],
381 &s,
382 num_stationary,
383 ))
384 } else {
385 None
386 };
387 if verboser.is_enabled(Verbosity::Trace) {
388 crate::verbosity_log!(
389 crate::diag::Verbosity::Summary,
390 "copp3_socp: allow(status)={}, extracted_profile={}",
391 options.is_allow(solution.status),
392 if result.is_some() {
393 "Some(Topp3Profile)"
394 } else {
395 "None"
396 }
397 );
398 }
399 Ok(ClarabelExpertInfor3rd {
400 result,
401 solution,
402 linsolver,
403 })
404}
405
406#[inline(always)]
408fn length_xi(n: usize, num_stationary: (usize, usize)) -> usize {
409 n + 1 - num_stationary.0.max(1) - num_stationary.1.max(1)
410}
411
412#[inline(always)]
414fn skip_a_for_xi(num_stationary_start: usize) -> usize {
415 num_stationary_start.max(1)
416}
417
418fn clarabel_sqrt_a_copp3(
424 n: usize,
425 objective: &[CoppObjective],
426 constraints: ConstraintsClarabel,
427 num_stationary: (usize, usize),
428) -> usize {
429 let (row, col, val, b, cones) = constraints;
430 for obj in objective {
431 match obj {
432 CoppObjective::Time(_) | CoppObjective::ThermalEnergy(_, _) => {
433 let n_skip = skip_a_for_xi(num_stationary.0);
434 let len_xi = length_xi(n, num_stationary); row.extend(b.len()..b.len() + len_xi);
438 col.extend((2 * (n + 1))..(2 * (n + 1) + len_xi));
439 val.resize(val.len() + len_xi, -1.0);
440 b.resize(b.len() + len_xi, 0.0);
441 cones.push(NonnegativeConeT(len_xi));
442 row.extend(b.len()..b.len() + 3 * len_xi);
448 val.resize(val.len() + 3 * len_xi, -1.0);
449 cones.resize(cones.len() + len_xi, SecondOrderConeT(3));
450 for k in 0..len_xi {
451 col.extend([k + n_skip, k + n_skip, 2 * (n + 1) + k]);
452 b.extend([0.25, -0.25, 0.0]);
453 }
454 return 2 * (n + 1) + len_xi;
455 }
456 _ => {}
457 }
458 }
459 2 * (n + 1)
460}
461
462fn clarabel_objective_capacity_copp3<M: RobotBasic>(
464 n: usize,
465 objective: &[CoppObjective],
466 robot: &Robot<M>,
467) -> (usize, usize, usize, usize) {
468 let (mut capacity_val, mut capacity_b, mut capacity_cones, mut n_vars) =
471 if objective.iter().any(|obj| {
472 matches!(
473 obj,
474 CoppObjective::Time(_) | CoppObjective::ThermalEnergy(_, _)
475 )
476 }) {
477 (4 * n, 4 * n, n, 2 * (n + 1))
478 } else {
479 (0, 0, 0, 2 * (n + 1))
480 };
481 let dim = robot.dim();
483 for obj in objective {
484 match obj {
485 CoppObjective::Time(_) => {
486 capacity_val += 5 * n;
488 capacity_b += 4 * n;
489 capacity_cones += n;
490 n_vars += n;
491 }
492 CoppObjective::ThermalEnergy(_, _) => {
493 capacity_val += (4 + 2 * dim) * (n + 1);
495 capacity_b += (dim + 2) * (n + 1);
496 capacity_cones += n + 1;
497 n_vars += n + 1;
498 }
499 CoppObjective::TotalVariationTorque(_, _) => {
500 capacity_val += 10 * dim * n;
502 capacity_b += 2 * dim * n;
503 capacity_cones += 1;
504 n_vars += dim * n;
505 }
506 _ => {}
507 }
508 }
509 (capacity_val, capacity_b, capacity_cones, n_vars)
510}
511
512fn clarabel_objective_copp3<M: RobotTorque>(
513 problem: &Copp3Problem<M>,
514 num_stationary: (usize, usize),
515 objective_constraints: ObjConsClarabel,
516) -> Result<(), CoppError> {
517 let (row, col, val, b, cones, q_object) = objective_constraints;
518 let n = problem.a_linearization.len() - 1;
519 let s = problem
520 .robot
521 .constraints
522 .s_vec(problem.idx_s_start, problem.idx_s_start + n + 1)?;
523 let weight_a_time = if problem
524 .objectives
525 .iter()
526 .any(|obj| matches!(obj, CoppObjective::Time(_)))
527 {
528 get_weight_a_topp3(&s, num_stationary)
529 } else {
530 vec![]
531 };
532 let weight_a_torque = if problem
533 .objectives
534 .iter()
535 .any(|obj| matches!(obj, CoppObjective::ThermalEnergy(_, _)))
536 {
537 get_weight_a_copp3(&s, num_stationary)
538 } else {
539 vec![]
540 };
541 let coeffs_torque = if problem.objectives.iter().any(|obj| {
542 matches!(
543 obj,
544 CoppObjective::ThermalEnergy(_, _) | CoppObjective::TotalVariationTorque(_, _)
545 )
546 }) {
547 problem.robot.torque_coeff(problem.idx_s_start, n + 1)?
549 } else {
550 (
551 DMatrix::<f64>::zeros(0, 0),
552 DMatrix::<f64>::zeros(0, 0),
553 DMatrix::<f64>::zeros(0, 0),
554 )
555 };
556 for obj in problem.objectives {
557 match obj {
558 CoppObjective::Time(weight) => {
559 if !clarabel_objective_time_copp3(
560 &s,
561 *weight,
562 &weight_a_time,
563 num_stationary,
564 (row, col, val, b, cones, q_object),
565 ) {
566 return Err(CoppError::InvalidInput(
567 "clarabel_objective_copp3".into(),
568 "Invalid Time objective".into(),
569 ));
570 }
571 }
572 CoppObjective::ThermalEnergy(weight, normalize) => {
573 if !clarabel_objective_thermal_energy_copp3(
574 &weight_a_torque,
575 *weight,
576 normalize,
577 &coeffs_torque,
578 num_stationary,
579 (row, col, val, b, cones, q_object),
580 ) {
581 return Err(CoppError::InvalidInput(
582 "clarabel_objective_copp3".into(),
583 "Invalid ThermalEnergy objective".into(),
584 ));
585 }
586 }
587 CoppObjective::TotalVariationTorque(weight, normalize) => {
588 if !clarabel_objective_tv_torque_copp3(
589 *weight,
590 normalize,
591 &coeffs_torque,
592 num_stationary,
593 (row, col, val, b, cones, q_object),
594 ) {
595 return Err(CoppError::InvalidInput(
596 "clarabel_objective_copp3".into(),
597 "Invalid TotalVariationTorque objective".into(),
598 ));
599 }
600 }
601 CoppObjective::Linear(weight, alpha, beta) => {
602 if !clarabel_objective_linear_copp3(
603 &s,
604 *weight,
605 alpha,
606 beta,
607 q_object,
608 num_stationary,
609 ) {
610 return Err(CoppError::InvalidInput(
611 "clarabel_objective_copp3".into(),
612 "Invalid Linear objective".into(),
613 ));
614 }
615 }
616 }
617 }
618 Ok(())
619}
620
621fn clarabel_objective_time_copp3(
624 s: &[f64],
625 weight: f64,
626 weight_a: &[f64],
627 num_stationary: (usize, usize),
628 objective_constraints: ObjConsClarabel,
629) -> bool {
630 if weight < 0.0 {
631 return false;
632 }
633
634 let (row, col, val, b, cones, q_object) = objective_constraints;
635 let n = s.len() - 1;
636 let len_xi = length_xi(n, num_stationary);
637 let k_skip = skip_a_for_xi(num_stationary.0);
638 let id_xi_start = 2 * (n + 1);
642 let id_eta_start = q_object.len();
643 row.extend(b.len()..(b.len() + len_xi));
646 col.extend(id_eta_start..(id_eta_start + len_xi));
647 val.resize(val.len() + len_xi, -1.0);
648 b.resize(b.len() + len_xi, 0.0);
649 cones.push(NonnegativeConeT(len_xi));
650 for k in 0..len_xi {
653 col.extend([
655 id_xi_start + k,
656 id_eta_start + k,
657 id_xi_start + k,
658 id_eta_start + k,
659 ]);
660 row.extend([b.len(), b.len(), b.len() + 1, b.len() + 1]);
661 val.extend([-1.0, -1.0, -1.0, 1.0]);
662 b.extend([0.0, 0.0, 2.0]);
663 }
664 cones.resize(cones.len() + len_xi, SecondOrderConeT(3));
665 q_object.extend(
668 weight_a
669 .iter()
670 .skip(k_skip)
671 .take(len_xi)
672 .map(|w| weight * w),
673 );
674
675 true
676}
677
678fn clarabel_objective_thermal_energy_copp3(
681 weight_a: &[f64],
682 weight: f64,
683 normalize: &[f64],
684 coeffs_torque: &(DMatrix<f64>, DMatrix<f64>, DMatrix<f64>),
685 num_stationary: (usize, usize),
686 objective_constraints: ObjConsClarabel,
687) -> bool {
688 if weight < 0.0 {
689 return false;
690 }
691 let (row, col, val, b, cones, q_object) = objective_constraints;
692 let mut coeff_a = coeffs_torque.0.clone();
694 let mut coeff_b = coeffs_torque.1.clone();
695 let mut coeff_g = coeffs_torque.2.clone();
696 let dim = coeff_a.nrows();
697 if normalize.len() != dim {
698 return false;
699 }
700 let n = coeff_a.ncols() - 1;
701 let normalize = DVectorView::from_slice(normalize, dim);
703 for mut col in coeff_a.column_iter_mut() {
704 col.component_mul_assign(&normalize);
705 }
706 for mut col in coeff_b.column_iter_mut() {
707 col.component_mul_assign(&normalize);
708 }
709 for mut col in coeff_g.column_iter_mut() {
710 col.component_mul_assign(&normalize);
711 }
712 let k_skip = skip_a_for_xi(num_stationary.0);
714 let len_xi = length_xi(n, num_stationary);
715
716 if num_stationary.0 > 0 {
717 let col_a = coeff_a.column(num_stationary.0);
723 let col_b = coeff_b.column(num_stationary.0);
724 let col_g = coeff_g.column(num_stationary.0);
725 let col_g_0 = coeff_g.column(0);
726 let id_t = q_object.len();
732 row.extend(b.len()..(b.len() + 2));
734 col.resize(col.len() + 2, id_t);
735 row.extend(b.len()..(b.len() + 2));
736 col.resize(col.len() + 2, 2 * (n + 1));
737 val.resize(val.len() + 3, -1.0);
738 val.push(1.0);
739 b.resize(b.len() + 2, 0.0);
740 row.extend((b.len())..(b.len() + dim));
742 col.resize(col.len() + dim, num_stationary.0);
743 val.extend(col_a.iter().take(dim));
744 row.extend((b.len())..(b.len() + dim));
745 col.resize(col.len() + dim, n + 1 + num_stationary.0);
746 val.extend(col_b.iter().take(dim));
747 b.extend(
748 col_g
749 .iter()
750 .zip(col_g_0.iter())
751 .take(dim)
752 .map(|(&g, &g_0)| -(g + g_0)),
753 );
754 cones.push(SecondOrderConeT(dim + 2));
755 q_object.push(weight * weight_a[0]);
756 }
757 if num_stationary.1 > 0 {
758 let col_a = coeff_a.column(n - num_stationary.1);
764 let col_b = coeff_b.column(n - num_stationary.1);
765 let col_g = coeff_g.column(n - num_stationary.1);
766 let col_g_f = coeff_g.column(n);
767 let id_t = q_object.len();
773 row.extend(b.len()..(b.len() + 2));
775 col.resize(col.len() + 2, id_t);
776 row.extend(b.len()..(b.len() + 2));
777 col.resize(col.len() + 2, 2 * n + len_xi + 1);
778 val.resize(val.len() + 3, -1.0);
779 val.push(1.0);
780 b.resize(b.len() + 2, 0.0);
781 row.extend((b.len())..(b.len() + dim));
783 col.resize(col.len() + dim, n - num_stationary.1);
784 val.extend(col_a.iter().take(dim));
785 row.extend((b.len())..(b.len() + dim));
786 col.resize(col.len() + dim, 2 * n + 1 - num_stationary.1);
787 val.extend(col_b.iter().take(dim));
788 b.extend(
789 col_g
790 .iter()
791 .zip(col_g_f.iter())
792 .take(dim)
793 .map(|(&g, &g_f)| -(g + g_f)),
794 );
795 cones.push(SecondOrderConeT(dim + 2));
796 q_object.push(weight * weight_a[n]);
797 }
798
799 let id_t_start = q_object.len();
803 for (k, (col_a, col_b, col_g)) in izip!(
804 coeff_a.column_iter(),
805 coeff_b.column_iter(),
806 coeff_g.column_iter()
807 )
808 .skip(k_skip)
809 .take(len_xi)
810 .enumerate()
811 {
812 row.extend(b.len()..(b.len() + 2));
817 col.resize(col.len() + 2, id_t_start + k);
818 row.extend(b.len()..(b.len() + 2));
819 col.resize(col.len() + 2, 2 * (n + 1) + k);
820 val.resize(val.len() + 3, -1.0);
821 val.push(1.0);
822 b.resize(b.len() + 2, 0.0);
823 row.extend((b.len())..(b.len() + dim));
825 col.resize(col.len() + dim, k + k_skip);
826 val.extend(col_a.iter().take(dim));
827 row.extend((b.len())..(b.len() + dim));
828 col.resize(col.len() + dim, n + 1 + k + k_skip);
829 val.extend(col_b.iter().take(dim));
830 b.extend(col_g.iter().take(dim).map(|&g| -g));
831 }
832 cones.resize(cones.len() + len_xi, SecondOrderConeT(dim + 2));
833 let weight_four = 4.0 * weight;
837 q_object.extend(
838 weight_a
839 .iter()
840 .skip(k_skip)
841 .take(len_xi)
842 .map(|w| weight_four * w),
843 );
844 true
845}
846
847fn clarabel_objective_tv_torque_copp3(
850 weight: f64,
851 normalize: &[f64],
852 coeffs_torque: &(DMatrix<f64>, DMatrix<f64>, DMatrix<f64>),
853 num_stationary: (usize, usize),
854 objective_constraints: ObjConsClarabel,
855) -> bool {
856 if weight < 0.0 {
857 return false;
858 }
859 let (row, col, val, b, cones, q_object) = objective_constraints;
860 let mut coeff_a = coeffs_torque.0.clone();
863 let mut coeff_b = coeffs_torque.1.clone();
864 let mut coeff_g = coeffs_torque.2.clone();
865 let dim = coeff_a.nrows();
866 if normalize.len() != dim {
867 return false;
868 }
869 let n = coeff_a.ncols() - 1;
870 let normalize = DVectorView::from_slice(normalize, dim);
872 for mut col in coeff_a.column_iter_mut() {
873 col.component_mul_assign(&normalize);
874 }
875 for mut col in coeff_b.column_iter_mut() {
876 col.component_mul_assign(&normalize);
877 }
878 for mut col in coeff_g.column_iter_mut() {
879 col.component_mul_assign(&normalize);
880 }
881 let n_b_old = b.len();
885 if num_stationary.0 > 0 {
888 let col_a = coeff_a.column(num_stationary.0);
890 let col_b = coeff_b.column(num_stationary.0);
891 let col_g = coeff_g.column(num_stationary.0);
892 let col_g_0 = coeff_g.column(0);
893 let n_var_old = q_object.len();
894 for (i, (&v_a, &v_b, &v_g, &v_g_0)) in
895 izip!(col_a.iter(), col_b.iter(), col_g.iter(), col_g_0.iter())
896 .take(dim)
897 .enumerate()
898 {
899 row.resize(row.len() + 3, b.len());
902 col.extend([num_stationary.0, n + num_stationary.0 + 1, n_var_old + i]);
903 val.extend([v_a, v_b, -1.0]);
904 b.push(v_g - v_g_0);
905 row.resize(row.len() + 3, b.len());
907 col.extend([num_stationary.0, n + num_stationary.0 + 1, n_var_old + i]);
908 val.extend([-v_a, -v_b, -1.0]);
909 b.push(v_g_0 - v_g);
910 }
911 q_object.resize(q_object.len() + dim, weight);
912 }
913
914 if num_stationary.1 > 0 {
915 let col_a = coeff_a.column(n - num_stationary.1);
917 let col_b = coeff_b.column(n - num_stationary.1);
918 let col_g = coeff_g.column(n - num_stationary.1);
919 let col_g_f = coeff_g.column(n);
920 let n_var_old = q_object.len();
921 for (i, (&v_a, &v_b, &v_g, &v_g_f)) in
922 izip!(col_a.iter(), col_b.iter(), col_g.iter(), col_g_f.iter())
923 .take(dim)
924 .enumerate()
925 {
926 row.resize(row.len() + 3, b.len());
929 col.extend([
930 n - num_stationary.1,
931 n + n - num_stationary.1 + 1,
932 n_var_old + i,
933 ]);
934 val.extend([v_a, v_b, -1.0]);
935 b.push(v_g - v_g_f);
936 row.resize(row.len() + 3, b.len());
938 col.extend([
939 n - num_stationary.1,
940 n + n - num_stationary.1 + 1,
941 n_var_old + i,
942 ]);
943 val.extend([-v_a, -v_b, -1.0]);
944 b.push(v_g_f - v_g);
945 }
946 q_object.resize(q_object.len() + dim, weight);
947 }
948
949 for (k, ((col_a_curr, col_b_curr, col_g_curr), (col_a_next, col_b_next, col_g_next))) in izip!(
951 coeff_a.column_iter(),
952 coeff_b.column_iter(),
953 coeff_g.column_iter()
954 )
955 .tuple_windows()
956 .enumerate()
957 .skip(num_stationary.0)
958 .take(n - num_stationary.0 - num_stationary.1)
959 {
960 let n_var_old = q_object.len();
961 for (i, (&v_a_curr, &v_b_curr, &v_g_curr, &v_a_next, &v_b_next, &v_g_next)) in izip!(
962 col_a_curr.iter(),
963 col_b_curr.iter(),
964 col_g_curr.iter(),
965 col_a_next.iter(),
966 col_b_next.iter(),
967 col_g_next.iter()
968 )
969 .enumerate()
970 {
971 row.resize(row.len() + 5, b.len());
974 col.extend([k, k + 1, n + k + 1, n + k + 2, n_var_old + i]);
975 val.extend([-v_a_curr, v_a_next, -v_b_curr, v_b_next, -1.0]);
976 b.push(v_g_next - v_g_curr);
977 row.resize(row.len() + 5, b.len());
979 col.extend([k, k + 1, n + k + 1, n + k + 2, n_var_old + i]);
980 val.extend([v_a_curr, -v_a_next, v_b_curr, -v_b_next, -1.0]);
981 b.push(v_g_curr - v_g_next);
982 }
983 q_object.resize(q_object.len() + dim, weight);
984 }
985 cones.push(NonnegativeConeT(b.len() - n_b_old));
986
987 true
988}
989
990fn clarabel_objective_linear_copp3(
992 s: &[f64],
993 weight: f64,
994 alpha: &[f64],
995 beta: &[f64],
996 q_object: &mut [f64],
997 num_stationary: (usize, usize),
998) -> bool {
999 if alpha.len() != s.len() || beta.len() != s.len() {
1000 return false;
1001 }
1002 let n = s.len() - 1;
1003 if num_stationary.0 > 1 {
1005 let &s_start = s.first().unwrap();
1006 let ds_start = s[num_stationary.0] - s_start;
1007 let q_n1 = &mut q_object[num_stationary.0];
1008 for (&s_k, &alpha_k, &beta_k) in izip!(s.iter(), alpha.iter(), beta.iter())
1009 .skip(1)
1010 .take(num_stationary.0 - 1)
1011 {
1012 let dsk_start = s_k - s_start;
1013 let gamma = dsk_start / ds_start;
1014 *q_n1 += weight * gamma * gamma.cbrt() * (alpha_k + beta_k / (1.5 * dsk_start));
1017 }
1018 }
1019 if num_stationary.1 > 1 {
1020 let &s_final = s.last().unwrap();
1021 let ds_final = s[n - num_stationary.1] - s_final;
1022 let q_n2 = &mut q_object[n - num_stationary.1];
1023 for (&s_k, &alpha_k, &beta_k) in izip!(s.iter(), alpha.iter(), beta.iter())
1024 .skip(1)
1025 .take(num_stationary.1 - 1)
1026 {
1027 let dsk_final = s_k - s_final;
1028 let gamma = dsk_final / ds_final;
1029 *q_n2 += weight * gamma * gamma.cbrt() * (alpha_k + beta_k / (1.5 * dsk_final));
1032 }
1033 }
1034 for (q_k, &alpha_k) in q_object
1035 .iter_mut()
1036 .zip(alpha.iter())
1037 .take(n + 1 - num_stationary.1)
1038 .skip(num_stationary.0)
1039 {
1040 *q_k += weight * alpha_k;
1041 }
1042 for (q_k, &beta_k) in q_object
1043 .iter_mut()
1044 .skip(n + 1)
1045 .zip(beta.iter())
1046 .take(n + 1 - num_stationary.1)
1047 .skip(num_stationary.0)
1048 {
1049 *q_k += weight * beta_k;
1050 }
1051
1052 true
1053}
1054
1055#[inline(always)]
1058fn objective_value_time_copp3(
1059 a_sqrt_down: &[f64],
1060 weight_a: &[f64],
1061 num_stationary: (usize, usize),
1062) -> f64 {
1063 let n = a_sqrt_down.len() - 1;
1064 let k_skip = skip_a_for_xi(num_stationary.0);
1065 let len_xi = length_xi(n, num_stationary);
1066 let mut objective = 0.0;
1068 for (a_sqrt_down, weight_a) in a_sqrt_down
1069 .iter()
1070 .zip(weight_a.iter())
1071 .skip(k_skip)
1072 .take(len_xi)
1073 {
1074 objective += weight_a * a_sqrt_down;
1075 }
1076 objective
1077}
1078
1079#[inline(always)]
1081fn objective_value_thermal_energy_copp3(
1082 a_sqrt_down: &[f64],
1083 weight_a: &[f64],
1084 num_stationary: (usize, usize),
1085 torque: &DMatrix<f64>,
1086 normalize: &[f64],
1087) -> f64 {
1088 let mut objective = 0.0;
1089 let n = a_sqrt_down.len() - 1;
1090 if num_stationary.0 > 0 {
1091 let torque_n1 = torque.column(num_stationary.0);
1094 let torque_0 = torque.column(0);
1095 let weight = weight_a[0] * a_sqrt_down[num_stationary.0];
1096 for (tau_n1, tau_0, &normalize_i) in
1097 izip!(torque_n1.iter(), torque_0.iter(), normalize.iter())
1098 {
1099 let tau_average = 0.5 * normalize_i * (tau_n1 + tau_0);
1100 objective += weight * tau_average * tau_average;
1101 }
1102 }
1103 if num_stationary.1 > 0 {
1104 let torque_n2 = torque.column(n - num_stationary.1);
1107 let torque_n = torque.column(n);
1108 let weight = weight_a[n] * a_sqrt_down[n - num_stationary.1];
1109 for (tau_n2, tau_n, &normalize_i) in
1110 izip!(torque_n2.iter(), torque_n.iter(), normalize.iter())
1111 {
1112 let tau_average = 0.5 * normalize_i * (tau_n2 + tau_n);
1113 objective += weight * tau_average * tau_average;
1114 }
1115 }
1116 let k_skip = skip_a_for_xi(num_stationary.0);
1117 let len_xi = length_xi(n, num_stationary);
1118 for (torque_k, &a_sqrt_down_k, &weight_a_k) in
1119 izip!(torque.column_iter(), a_sqrt_down.iter(), weight_a.iter())
1120 .skip(k_skip)
1121 .take(len_xi)
1122 {
1123 for (tau_k, &normalize_i) in torque_k.iter().zip(normalize.iter()) {
1124 let tau_normal = tau_k * normalize_i;
1125 objective += 4.0 * weight_a_k * tau_normal * tau_normal * a_sqrt_down_k;
1126 }
1127 }
1128
1129 objective
1130}
1131
1132#[inline(always)]
1134fn objective_value_tv_torque_copp3(
1135 torque: &DMatrix<f64>,
1136 normalize: &[f64],
1137 num_stationary: (usize, usize),
1138) -> f64 {
1139 let mut objective = 0.0;
1140 let n = torque.ncols() - 1;
1141 if num_stationary.0 > 0 {
1142 let torque_n1 = torque.column(num_stationary.0);
1144 let torque_0 = torque.column(0);
1145 for (tau_n1, tau_0, &normalize_i) in
1146 izip!(torque_n1.iter(), torque_0.iter(), normalize.iter())
1147 {
1148 objective += normalize_i * (tau_n1 - tau_0).abs();
1149 }
1150 }
1151 if num_stationary.1 > 0 {
1152 let torque_n2 = torque.column(n - num_stationary.1);
1154 let torque_n = torque.column(n);
1155 for (tau_n2, tau_n, &normalize_i) in
1156 izip!(torque_n2.iter(), torque_n.iter(), normalize.iter())
1157 {
1158 objective += normalize_i * (tau_n2 - tau_n).abs();
1159 }
1160 }
1161 for (torque_col_curr, torque_col_next) in torque
1163 .column_iter()
1164 .tuple_windows()
1165 .skip(num_stationary.0)
1166 .take(n - num_stationary.0 - num_stationary.1)
1167 {
1168 for (tau_k, tau_k_next, &normalize_i) in izip!(
1169 torque_col_curr.iter(),
1170 torque_col_next.iter(),
1171 normalize.iter()
1172 ) {
1173 objective += normalize_i * (tau_k_next - tau_k).abs();
1174 }
1175 }
1176
1177 objective
1178}
1179
1180#[inline(always)]
1182fn objective_value_linear_copp3(
1183 a_profile: &[f64],
1184 b_profile: &[f64],
1185 alpha: &[f64],
1186 beta: &[f64],
1187) -> f64 {
1188 let mut objective = 0.0;
1190 for (a_curr, alpha_curr) in a_profile.iter().zip(alpha.iter()) {
1191 objective += a_curr * alpha_curr;
1193 }
1194 for (b_curr, beta_curr) in b_profile.iter().zip(beta.iter()) {
1195 objective += b_curr * beta_curr;
1197 }
1198 objective
1199}
1200
1201pub(crate) fn objective_value_copp3_opt<M: RobotTorque>(
1203 problem: &Copp3Problem<M>,
1204 profile: Topp3ProfileRef<'_>,
1205) -> (f64, Vec<f64>) {
1206 let (a_profile, b_profile, num_stationary) = profile;
1207 let s = problem
1208 .robot
1209 .constraints
1210 .s_vec(problem.idx_s_start, problem.idx_s_start + a_profile.len());
1211 let Ok(s) = s else {
1212 return (f64::INFINITY, vec![0.0; problem.objectives.len()]);
1213 };
1214 if a_profile.len() != s.len() || b_profile.len() != s.len() {
1215 return (f64::INFINITY, vec![0.0; problem.objectives.len()]);
1216 }
1217 let (a_sqrt_down, weight_a_time) = if problem.objectives.iter().any(|obj| {
1218 matches!(
1219 obj,
1220 CoppObjective::Time(_) | CoppObjective::ThermalEnergy(_, _)
1221 )
1222 }) {
1223 (
1224 a_profile
1225 .iter()
1226 .map(|a| 1.0 / a.sqrt().max(1E-16))
1227 .collect(),
1228 get_weight_a_topp3(&s, num_stationary),
1229 )
1230 } else {
1231 (vec![], vec![])
1232 };
1233 let weight_a_torque = if problem
1234 .objectives
1235 .iter()
1236 .any(|obj| matches!(obj, CoppObjective::ThermalEnergy(_, _)))
1237 {
1238 get_weight_a_copp3(&s, num_stationary)
1239 } else {
1240 vec![]
1241 };
1242 let torque = if problem.objectives.iter().any(|obj| {
1243 matches!(
1244 obj,
1245 CoppObjective::ThermalEnergy(_, _) | CoppObjective::TotalVariationTorque(_, _)
1246 )
1247 }) {
1248 let torque_result =
1249 problem
1250 .robot
1251 .get_torque_with_ab(a_profile, b_profile, problem.idx_s_start);
1252 match torque_result {
1253 Ok(torque) => torque,
1254 _ => return (f64::INFINITY, vec![0.0; problem.objectives.len()]),
1255 }
1256 } else {
1257 DMatrix::<f64>::zeros(0, 0)
1258 };
1259 let mut obj_val = Vec::with_capacity(problem.objectives.len());
1260 let mut obj_val_total = 0.0;
1261 for obj in problem.objectives {
1262 match obj {
1263 CoppObjective::Time(weight) => {
1264 let obj_here =
1265 objective_value_time_copp3(&a_sqrt_down, &weight_a_time, num_stationary);
1266 obj_val.push(obj_here);
1267 obj_val_total += weight * obj_here;
1268 }
1269 CoppObjective::ThermalEnergy(weight, normalize) => {
1270 let obj_here = objective_value_thermal_energy_copp3(
1271 &a_sqrt_down,
1272 &weight_a_torque,
1273 num_stationary,
1274 &torque,
1275 normalize,
1276 );
1277 obj_val.push(obj_here);
1278 obj_val_total += weight * obj_here;
1279 }
1280 CoppObjective::TotalVariationTorque(weight, normalize) => {
1281 let obj_here = objective_value_tv_torque_copp3(&torque, normalize, num_stationary);
1282 obj_val.push(obj_here);
1283 obj_val_total += weight * obj_here;
1284 }
1285 CoppObjective::Linear(weight, alpha, beta) => {
1286 let obj_here = objective_value_linear_copp3(a_profile, b_profile, alpha, beta);
1287 obj_val.push(obj_here);
1288 obj_val_total += weight * obj_here;
1289 }
1290 }
1291 }
1292 (obj_val_total, obj_val)
1293}
1294
1295#[cfg(test)]
1296mod tests {
1297 use super::*;
1298 use crate::copp::copp2::stable::basic::{Topp2ProblemBuilder, s_to_t_topp2};
1299 use crate::copp::copp2::stable::reach_set2::ReachSet2OptionsBuilder;
1300 use crate::copp::copp2::stable::topp2_ra::topp2_ra;
1301 use crate::copp::copp3::stable::basic::{Copp3ProblemBuilder, s_to_t_topp3};
1302 use crate::copp::copp3::stable::topp3_lp::topp3_lp;
1303 use crate::copp::copp3::stable::topp3_socp::topp3_socp;
1304 use crate::copp::{ClarabelOptionsBuilder, default_clarabel_settings};
1305 use crate::path::{add_symmetric_axial_limits_for_test, lissajous_path_for_test};
1306 use crate::robot::robot_core::Robot;
1307 use std::time::Instant;
1308 use std::vec;
1309
1310 #[test]
1311 fn test_copp3_lp() -> Result<(), CoppError> {
1312 run_test_copp3_lp_repeated(1, false)
1313 }
1314
1315 #[test]
1318 #[ignore = "slow"]
1319 fn test_copp3_lp_robust() -> Result<(), CoppError> {
1320 run_test_copp3_lp_repeated(100, true)
1321 }
1322
1323 #[test]
1324 fn test_copp3_qp() -> Result<(), CoppError> {
1325 run_test_copp3_qp_repeated(1, false)
1326 }
1327
1328 #[test]
1331 #[ignore = "slow"]
1332 fn test_copp3_qp_robust() -> Result<(), CoppError> {
1333 run_test_copp3_qp_repeated(100, true)
1334 }
1335
1336 #[test]
1337 fn test_all_objectives() -> Result<(), CoppError> {
1338 run_test_all_objectives_repeated(1, false)
1339 }
1340
1341 #[test]
1349 #[ignore = "slow"]
1350 fn test_all_objectives_robust() -> Result<(), CoppError> {
1351 run_test_all_objectives_repeated(100, true)
1352 }
1353
1354 fn run_test_copp3_lp_repeated(n_exp: usize, flag_print_step: bool) -> Result<(), CoppError> {
1355 let mut tc_sum_ra = 0.0;
1356 let mut tc_sum_lp = 0.0;
1357 let mut tc_sum_copp = 0.0;
1358 let mut tf_sum_ra = 0.0;
1359 let mut tf_sum_lp = 0.0;
1360 let mut tf_sum_copp = 0.0;
1361 let mut obj_sum_lp = 0.0;
1362 let mut obj_sum_copp = 0.0;
1363
1364 for i_exp in 0..n_exp {
1365 let n: usize = 1000;
1366 let dim = 7;
1367 let mut robot = Robot::with_capacity(dim, n);
1368
1369 let mut rng = rand::rng();
1370 let (s, path, omega, phi) =
1371 lissajous_path_for_test(dim, n, &mut rng).expect("random range is valid");
1372 robot
1373 .with_s(&s.as_view())?
1374 .with_q_from_path_3rd(&path, 0, n)?;
1375 add_symmetric_axial_limits_for_test(&mut robot, 1.0, 1.0, Some(5.0))?;
1376
1377 let topp2_problem = Topp2ProblemBuilder::new(&robot, (0, n - 1), (0.0, 0.0)).build()?;
1378 let start = Instant::now();
1380 let options_ra0 = ReachSet2OptionsBuilder::new()
1381 .lp_feas_tol(1E-9)
1382 .a_cmp_abs_tol(1E-9)
1383 .a_cmp_rel_tol(1E-9)
1384 .build()?;
1385 let a_ra0 = topp2_ra(&topp2_problem, &options_ra0)?;
1386 let tc_ra0 = start.elapsed().as_secs_f64() * 1E3;
1387 let (tf_ra0, _) = s_to_t_topp2(s.as_slice(), &a_ra0, 0.0)?;
1388
1389 let objectives = [CoppObjective::Linear(
1390 1.0,
1391 &get_weight_a_topp3(&s.as_slice()[0..n], (1, 1))
1392 .iter()
1393 .map(|&w_a| -w_a)
1394 .collect_vec(),
1395 &vec![0.0; n],
1396 )];
1397 let copp3_problem = Copp3ProblemBuilder::new(
1398 &mut robot,
1399 &objectives,
1400 0,
1401 &a_ra0,
1402 (0.0, 0.0),
1403 (0.0, 0.0),
1404 )
1405 .build_with_linearization()?;
1406
1407 let start = Instant::now();
1409 let profile_copp = {
1410 let mut settings = default_clarabel_settings();
1411 settings.tol_gap_rel = 1E-6;
1412 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1413 .allow_almost_solved(true)
1414 .build()?;
1415 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1416 if let Some(result) = result {
1417 result
1418 } else {
1419 return Err(CoppError::ClarabelSolverStatus(
1420 "copp3_socp".into(),
1421 solution.status,
1422 ));
1423 }
1424 };
1425 let tc_copp = start.elapsed().as_secs_f64() * 1E3;
1426 let (tf_copp, _) = s_to_t_topp3(s.as_slice(), profile_copp.as_parts(), 0.0)?;
1428
1429 let options_lp = ClarabelOptionsBuilder::new()
1431 .allow_almost_solved(true)
1432 .build()?;
1433 let start = Instant::now();
1434 let profile_lp = topp3_lp(&copp3_problem.as_topp3_problem(), &options_lp)?;
1435 let tc_lp = start.elapsed().as_secs_f64() * 1E3;
1436 let (tf_lp, _) = s_to_t_topp3(s.as_slice(), profile_lp.as_parts(), 0.0)?;
1438
1439 let (obj_lp, _) = objective_value_copp3_opt(&copp3_problem, profile_lp.as_parts());
1440 let (obj_copp, _) = objective_value_copp3_opt(&copp3_problem, profile_copp.as_parts());
1441
1442 if flag_print_step {
1443 crate::verbosity_log!(
1444 crate::diag::Verbosity::Summary,
1445 "Exp #{}: tc_ra = {:.4} ms, tc_lp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_lp = {:.6}, tf_copp = {:.6}, obj_lp = {:.6}, obj_copp = {:.6}",
1446 i_exp + 1,
1447 tc_ra0,
1448 tc_lp,
1449 tc_copp,
1450 tf_ra0,
1451 tf_lp,
1452 tf_copp,
1453 obj_lp,
1454 obj_copp
1455 );
1456 }
1457
1458 if (tf_copp - tf_lp).abs() > 1e-8 {
1459 crate::verbosity_log!(
1460 crate::diag::Verbosity::Summary,
1461 "omega = {omega:?}\nphi = {phi:?}"
1462 );
1463 crate::verbosity_log!(
1464 crate::diag::Verbosity::Debug,
1465 "COPP3 time optimality failed! tf_copp - tf_lp = {}",
1466 tf_copp - tf_lp
1467 );
1468 }
1469
1470 tc_sum_ra += tc_ra0;
1471 tc_sum_lp += tc_lp;
1472 tc_sum_copp += tc_copp;
1473 tf_sum_ra += tf_ra0;
1474 tf_sum_lp += tf_lp;
1475 tf_sum_copp += tf_copp;
1476 obj_sum_lp += obj_lp;
1477 obj_sum_copp += obj_copp;
1478 }
1479
1480 crate::verbosity_log!(
1481 crate::diag::Verbosity::Summary,
1482 "Average {} experiments: tc_ra = {:.4} ms, tc_lp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_lp = {:.6}, tf_copp = {:.6}, obj_lp = {:.6}, obj_copp = {:.6}",
1483 n_exp,
1484 tc_sum_ra / n_exp as f64,
1485 tc_sum_lp / n_exp as f64,
1486 tc_sum_copp / n_exp as f64,
1487 tf_sum_ra / n_exp as f64,
1488 tf_sum_lp / n_exp as f64,
1489 tf_sum_copp / n_exp as f64,
1490 obj_sum_lp / n_exp as f64,
1491 obj_sum_copp / n_exp as f64
1492 );
1493
1494 Ok(())
1495 }
1496
1497 fn run_test_copp3_qp_repeated(n_exp: usize, flag_print_step: bool) -> Result<(), CoppError> {
1498 let mut tc_sum_ra = 0.0;
1499 let mut tc_sum_qp = 0.0;
1500 let mut tc_sum_copp = 0.0;
1501 let mut tf_sum_ra = 0.0;
1502 let mut tf_sum_qp = 0.0;
1503 let mut tf_sum_copp = 0.0;
1504 let mut obj_sum_qp = 0.0;
1505 let mut obj_sum_copp = 0.0;
1506 let mut succeed = 0;
1507
1508 for i_exp in 0..n_exp {
1509 let n: usize = 1000;
1510 let dim = 7;
1511 let mut robot = Robot::with_capacity(dim, n);
1512
1513 let mut rng = rand::rng();
1514 let (s, path, omega, phi) =
1515 lissajous_path_for_test(dim, n, &mut rng).expect("random range is valid");
1516 robot
1517 .with_s(&s.as_view())?
1518 .with_q_from_path_3rd(&path, 0, n)?;
1519 add_symmetric_axial_limits_for_test(&mut robot, 1.0, 1.0, Some(5.0))?;
1520
1521 let topp2_problem = Topp2ProblemBuilder::new(&robot, (0, n - 1), (0.0, 0.0)).build()?;
1522 let start = Instant::now();
1524 let options_ra0 = ReachSet2OptionsBuilder::new()
1525 .lp_feas_tol(1E-9)
1526 .a_cmp_abs_tol(1E-9)
1527 .a_cmp_rel_tol(1E-9)
1528 .build()?;
1529 let a_ra0 = topp2_ra(&topp2_problem, &options_ra0)?;
1530 let tc_ra0 = start.elapsed().as_secs_f64() * 1E3;
1531 let (tf_ra0, _) = s_to_t_topp2(s.as_slice(), &a_ra0, 0.0)?;
1532
1533 let objective = [CoppObjective::Time(1.0)];
1534 let copp3_problem =
1535 Copp3ProblemBuilder::new(&mut robot, &objective, 0, &a_ra0, (0.0, 0.0), (0.0, 0.0))
1536 .build_with_linearization()?;
1537
1538 let start = Instant::now();
1540 let profile_copp = {
1541 let mut settings = default_clarabel_settings();
1542 settings.tol_gap_rel = 1E-6;
1543 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1544 .allow_almost_solved(true)
1545 .build()?;
1546 let (result, solution) = match copp3_socp_expert(&copp3_problem, &options) {
1547 Ok(res) => res,
1548 Err(_) => {
1549 crate::verbosity_log!(
1550 crate::diag::Verbosity::Debug,
1551 "Exp #{}: Clarabel solver failed in copp3_socp_expert!",
1552 i_exp + 1
1553 );
1554 continue;
1555 }
1556 };
1557 if let Some(result) = result {
1558 result
1559 } else {
1560 return Err(CoppError::ClarabelSolverStatus(
1561 "copp3_socp".into(),
1562 solution.status,
1563 ));
1564 }
1565 };
1566 let tc_copp = start.elapsed().as_secs_f64() * 1E3;
1567 let (tf_copp, _) = s_to_t_topp3(s.as_slice(), profile_copp.as_parts(), 0.0)?;
1569
1570 let start = Instant::now();
1572 let options_qp = ClarabelOptionsBuilder::new()
1573 .allow_almost_solved(true)
1574 .build()?;
1575 let profile_qp = topp3_socp(&copp3_problem.as_topp3_problem(), &options_qp)?;
1576 let tc_qp = start.elapsed().as_secs_f64() * 1E3;
1577 let (tf_qp, _) = s_to_t_topp3(s.as_slice(), profile_qp.as_parts(), 0.0)?;
1579
1580 let (obj_qp, _) = objective_value_copp3_opt(&copp3_problem, profile_qp.as_parts());
1581 let (obj_copp, _) = objective_value_copp3_opt(&copp3_problem, profile_copp.as_parts());
1582
1583 if flag_print_step {
1584 crate::verbosity_log!(
1585 crate::diag::Verbosity::Summary,
1586 "Exp #{}: tc_ra = {:.4} ms, tc_qp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_qp = {:.6}, tf_copp = {:.6}, obj_qp = {:.6}, obj_copp = {:.6}",
1587 i_exp + 1,
1588 tc_ra0,
1589 tc_qp,
1590 tc_copp,
1591 tf_ra0,
1592 tf_qp,
1593 tf_copp,
1594 obj_qp,
1595 obj_copp
1596 );
1597 }
1598
1599 if (tf_copp - tf_qp).abs() > 1e-4 || (obj_copp - obj_qp).abs() > 1e-4 {
1600 crate::verbosity_log!(
1601 crate::diag::Verbosity::Summary,
1602 "omega = {omega:?}\nphi = {phi:?}"
1603 );
1604 crate::verbosity_log!(
1605 crate::diag::Verbosity::Debug,
1606 "COPP3 time optimality failed at Exp #{}! tf_copp - tf_qp = {}, obj_copp - obj_qp = {}",
1607 i_exp + 1,
1608 tf_copp - tf_qp,
1609 obj_copp - obj_qp
1610 );
1611 }
1612
1613 tc_sum_ra += tc_ra0;
1614 tc_sum_qp += tc_qp;
1615 tc_sum_copp += tc_copp;
1616 tf_sum_ra += tf_ra0;
1617 tf_sum_qp += tf_qp;
1618 tf_sum_copp += tf_copp;
1619 obj_sum_qp += obj_qp;
1620 obj_sum_copp += obj_copp;
1621 succeed += 1;
1622 }
1623
1624 crate::verbosity_log!(
1625 crate::diag::Verbosity::Summary,
1626 "Average {n_exp} experiments (fail {}): tc_ra = {:.4} ms, tc_qp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_qp = {:.6}, tf_copp = {:.6}, obj_qp = {:.6}, obj_copp = {:.6}",
1627 n_exp - succeed,
1628 tc_sum_ra / succeed as f64,
1629 tc_sum_qp / succeed as f64,
1630 tc_sum_copp / succeed as f64,
1631 tf_sum_ra / succeed as f64,
1632 tf_sum_qp / succeed as f64,
1633 tf_sum_copp / succeed as f64,
1634 obj_sum_qp / succeed as f64,
1635 obj_sum_copp / succeed as f64
1636 );
1637
1638 Ok(())
1639 }
1640
1641 fn run_test_all_objectives_repeated(
1642 n_exp: usize,
1643 flag_print_step: bool,
1644 ) -> Result<(), CoppError> {
1645 let mut tc_sum_case0 = 0.0;
1646 let mut tc_sum_case1 = 0.0;
1647 let mut tc_sum_case2 = 0.0;
1648 let mut tc_sum_case3 = 0.0;
1649 let mut tc_sum_case4 = 0.0;
1650 let mut obj_sum_case0 = vec![0.0; 4];
1651 let mut obj_sum_case1 = vec![0.0; 4];
1652 let mut obj_sum_case2 = vec![0.0; 4];
1653 let mut obj_sum_case3 = vec![0.0; 4];
1654 let mut obj_sum_case4 = vec![0.0; 4];
1655
1656 let mut succeed = 0;
1657
1658 for i_exp in 0..n_exp {
1659 let n: usize = 1000;
1660 let dim = 7;
1661 let mut robot = Robot::with_capacity(dim, n);
1662
1663 let mut rng = rand::rng();
1664 let (s, path, omega, phi) =
1665 lissajous_path_for_test(dim, n, &mut rng).expect("random range is valid");
1666
1667 if flag_print_step {
1668 crate::verbosity_log!(
1669 crate::diag::Verbosity::Summary,
1670 "omega = {omega:?}\nphi = {phi:?}"
1671 );
1672 }
1673 robot
1674 .with_s(&s.as_view())?
1675 .with_q_from_path_3rd(&path, 0, n)?;
1676 add_symmetric_axial_limits_for_test(&mut robot, 1.0, 1.0, Some(5.0))?;
1677
1678 let topp2_problem = Topp2ProblemBuilder::new(&robot, (0, n - 1), (0.0, 0.0)).build()?;
1679 let options_ra0 = ReachSet2OptionsBuilder::new()
1681 .lp_feas_tol(1E-9)
1682 .a_cmp_abs_tol(1E-9)
1683 .a_cmp_rel_tol(1E-9)
1684 .build()?;
1685 let a_ra0 = topp2_ra(&topp2_problem, &options_ra0)?;
1686
1687 let objectives_test = [
1689 CoppObjective::Time(1.0),
1690 CoppObjective::ThermalEnergy(1.0, &vec![1.0; dim]),
1691 CoppObjective::TotalVariationTorque(1.0, &vec![1.0; dim]),
1692 CoppObjective::Linear(1.0, &vec![0.0; n], &vec![1.0; n - 1]),
1693 ];
1694
1695 let obj_case0_src = [CoppObjective::Time(1.0)];
1697 let start = Instant::now();
1698 let (a_case0, b_case0, num_stationary) = {
1699 let copp3_problem = Copp3ProblemBuilder::new(
1700 &mut robot,
1701 &obj_case0_src,
1702 0,
1703 &a_ra0,
1704 (0.0, 0.0),
1705 (0.0, 0.0),
1706 )
1707 .build_with_linearization()?;
1708 let mut settings = default_clarabel_settings();
1709 settings.tol_gap_rel = 1E-6;
1710 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1711 .allow_almost_solved(true)
1712 .build()?;
1713 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1714 if let Some(result) = result {
1715 result.into_parts()
1716 } else {
1717 crate::verbosity_log!(
1718 crate::diag::Verbosity::Debug,
1719 "{:?}",
1720 CoppError::ClarabelSolverStatus(
1721 "copp3_socp (case 0)".into(),
1722 solution.status,
1723 )
1724 );
1725 continue;
1726 }
1727 };
1728 let tc_copp3_case0 = start.elapsed().as_secs_f64() * 1E3;
1729 let (_, obj_case0) = {
1730 let copp3_problem = Copp3ProblemBuilder::new(
1731 &mut robot,
1732 &objectives_test,
1733 0,
1734 &a_ra0,
1735 (0.0, 0.0),
1736 (0.0, 0.0),
1737 )
1738 .build_with_linearization()?;
1739 objective_value_copp3_opt(&copp3_problem, (&a_case0, &b_case0, num_stationary))
1740 };
1741
1742 let obj_case1 = [
1744 CoppObjective::Time(1.0),
1745 CoppObjective::ThermalEnergy(1.0, &vec![1.0; dim]),
1746 ];
1747 let start = Instant::now();
1748 let (a_case1, b_case1, num_stationary) = {
1749 let copp3_problem = Copp3ProblemBuilder::new(
1750 &mut robot,
1751 &obj_case1,
1752 0,
1753 &a_ra0,
1754 (0.0, 0.0),
1755 (0.0, 0.0),
1756 )
1757 .build_with_linearization()?;
1758 let mut settings = default_clarabel_settings();
1759 settings.tol_gap_rel = 1E-6;
1760 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1761 .allow_almost_solved(true)
1762 .build()?;
1763 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1764 if let Some(result) = result {
1765 result.into_parts()
1766 } else {
1767 crate::verbosity_log!(
1768 crate::diag::Verbosity::Debug,
1769 "{:?}",
1770 CoppError::ClarabelSolverStatus(
1771 "copp3_socp (case 1)".into(),
1772 solution.status,
1773 )
1774 );
1775 continue;
1776 }
1777 };
1778 let tc_copp3_case1 = start.elapsed().as_secs_f64() * 1E3;
1779 let (_, obj_case1) = {
1780 let copp3_problem = Copp3ProblemBuilder::new(
1781 &mut robot,
1782 &objectives_test,
1783 0,
1784 &a_ra0,
1785 (0.0, 0.0),
1786 (0.0, 0.0),
1787 )
1788 .build_with_linearization()?;
1789 objective_value_copp3_opt(&copp3_problem, (&a_case1, &b_case1, num_stationary))
1790 };
1791 if obj_case1[0] < obj_case0[0] - 1E-3 || obj_case1[1] - 1E-3 > obj_case0[1] {
1792 let (tf_case0, _) = s_to_t_topp2(s.as_slice(), &a_case0, 0.0)?;
1793 let (tf_case1, _) = s_to_t_topp2(s.as_slice(), &a_case1, 0.0)?;
1794 crate::verbosity_log!(
1795 crate::diag::Verbosity::Summary,
1796 "omega = {omega:?}\nphi = {phi:?}"
1797 );
1798 crate::verbosity_log!(
1799 crate::diag::Verbosity::Summary,
1800 "Case 0: obj_time = {}, obj_thermal_energy = {}, tf = {}",
1801 obj_case0[0],
1802 obj_case0[1],
1803 tf_case0
1804 );
1805 crate::verbosity_log!(
1806 crate::diag::Verbosity::Summary,
1807 "Case 1: obj_time = {}, obj_thermal_energy = {}, tf = {}",
1808 obj_case1[0],
1809 obj_case1[1],
1810 tf_case1
1811 );
1812 crate::verbosity_log!(
1813 crate::diag::Verbosity::Summary,
1814 "Interesting... Cases 0 and 1"
1815 );
1816 }
1817
1818 let obj_case2 = [
1820 CoppObjective::Time(1.0),
1821 CoppObjective::ThermalEnergy(10.0, &vec![1.0; dim]),
1822 ];
1823 let start = Instant::now();
1824 let (a_case2, b_case2, num_stationary) = {
1825 let copp3_problem = Copp3ProblemBuilder::new(
1826 &mut robot,
1827 &obj_case2,
1828 0,
1829 &a_ra0,
1830 (0.0, 0.0),
1831 (0.0, 0.0),
1832 )
1833 .build_with_linearization()?;
1834 let mut settings = default_clarabel_settings();
1835 settings.tol_gap_rel = 1E-6;
1836 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1837 .allow_almost_solved(true)
1838 .build()?;
1839 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1840 if let Some(result) = result {
1841 result.into_parts()
1842 } else {
1843 crate::verbosity_log!(
1844 crate::diag::Verbosity::Debug,
1845 "{:?}",
1846 CoppError::ClarabelSolverStatus(
1847 "copp3_socp (case 2)".into(),
1848 solution.status,
1849 )
1850 );
1851 continue;
1852 }
1853 };
1854 let tc_copp3_case2 = start.elapsed().as_secs_f64() * 1E3;
1855 let (_, obj_case2) = {
1856 let copp3_problem = Copp3ProblemBuilder::new(
1857 &mut robot,
1858 &objectives_test,
1859 0,
1860 &a_ra0,
1861 (0.0, 0.0),
1862 (0.0, 0.0),
1863 )
1864 .build_with_linearization()?;
1865 objective_value_copp3_opt(&copp3_problem, (&a_case2, &b_case2, num_stationary))
1866 };
1867 if obj_case2[0] < obj_case1[0] - 1E-3 || obj_case2[1] - 1E-3 > obj_case1[1] {
1868 crate::verbosity_log!(
1869 crate::diag::Verbosity::Summary,
1870 "omega = {omega:?}\nphi = {phi:?}"
1871 );
1872 crate::verbosity_log!(
1873 crate::diag::Verbosity::Summary,
1874 "Case 1: obj_time = {}, obj_thermal_energy = {}",
1875 obj_case1[0],
1876 obj_case1[1]
1877 );
1878 crate::verbosity_log!(
1879 crate::diag::Verbosity::Summary,
1880 "Case 2: obj_time = {}, obj_thermal_energy = {}",
1881 obj_case2[0],
1882 obj_case2[1]
1883 );
1884 crate::verbosity_log!(
1885 crate::diag::Verbosity::Summary,
1886 "Interesting... Cases 1 and 2"
1887 );
1888 }
1889
1890 let obj_case3 = [
1892 CoppObjective::Time(1.0),
1893 CoppObjective::TotalVariationTorque(1.0, &vec![1.0; dim]),
1894 ];
1895 let start = Instant::now();
1896 let (a_case3, b_case3, num_stationary) = {
1897 let copp3_problem = Copp3ProblemBuilder::new(
1898 &mut robot,
1899 &obj_case3,
1900 0,
1901 &a_ra0,
1902 (0.0, 0.0),
1903 (0.0, 0.0),
1904 )
1905 .build_with_linearization()?;
1906 let mut settings = default_clarabel_settings();
1907 settings.tol_gap_rel = 1E-6;
1908 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1909 .allow_almost_solved(true)
1910 .build()?;
1911 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1912 if let Some(result) = result {
1913 result.into_parts()
1914 } else {
1915 crate::verbosity_log!(
1916 crate::diag::Verbosity::Debug,
1917 "{:?}",
1918 CoppError::ClarabelSolverStatus(
1919 "copp3_socp (case 3)".into(),
1920 solution.status,
1921 )
1922 );
1923 continue;
1924 }
1925 };
1926 let tc_copp3_case3 = start.elapsed().as_secs_f64() * 1E3;
1927 let (_, obj_case3) = {
1928 let copp3_problem = Copp3ProblemBuilder::new(
1929 &mut robot,
1930 &objectives_test,
1931 0,
1932 &a_ra0,
1933 (0.0, 0.0),
1934 (0.0, 0.0),
1935 )
1936 .build_with_linearization()?;
1937 objective_value_copp3_opt(&copp3_problem, (&a_case3, &b_case3, num_stationary))
1938 };
1939 if obj_case3[1] < obj_case1[1] - 1E-3 || obj_case3[2] - 1E-3 > obj_case1[2] {
1940 crate::verbosity_log!(
1941 crate::diag::Verbosity::Summary,
1942 "omega = {omega:?}\nphi = {phi:?}"
1943 );
1944 crate::verbosity_log!(
1945 crate::diag::Verbosity::Summary,
1946 "Case 1: obj_time = {}, obj_thermal_energy = {}, obj_total_variation_torque = {}",
1947 obj_case1[0],
1948 obj_case1[1],
1949 obj_case1[2]
1950 );
1951 crate::verbosity_log!(
1952 crate::diag::Verbosity::Summary,
1953 "Case 3: obj_time = {}, obj_thermal_energy = {}, obj_total_variation_torque = {}",
1954 obj_case3[0],
1955 obj_case3[1],
1956 obj_case3[2]
1957 );
1958 crate::verbosity_log!(
1959 crate::diag::Verbosity::Summary,
1960 "Interesting... Cases 1 and 3"
1961 );
1962 }
1963
1964 let obj_case4 = [
1966 CoppObjective::Time(1.0),
1967 CoppObjective::Linear(1.0, &vec![0.0; n], &vec![1.0; n]),
1968 ];
1969 let start = Instant::now();
1970 let (a_case4, b_case4, num_stationary) = {
1971 let copp3_problem = Copp3ProblemBuilder::new(
1972 &mut robot,
1973 &obj_case4,
1974 0,
1975 &a_ra0,
1976 (0.0, 0.0),
1977 (0.0, 0.0),
1978 )
1979 .build_with_linearization()?;
1980 let mut settings = default_clarabel_settings();
1981 settings.tol_gap_rel = 1E-6;
1982 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1983 .allow_almost_solved(true)
1984 .build()?;
1985 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1986 if let Some(result) = result {
1987 result.into_parts()
1988 } else {
1989 crate::verbosity_log!(
1990 crate::diag::Verbosity::Debug,
1991 "{:?}",
1992 CoppError::ClarabelSolverStatus(
1993 "copp3_socp (case 4)".into(),
1994 solution.status,
1995 )
1996 );
1997 continue;
1998 }
1999 };
2000 let tc_copp3_case4 = start.elapsed().as_secs_f64() * 1E3;
2001 let (_, obj_case4) = {
2002 let copp3_problem = Copp3ProblemBuilder::new(
2003 &mut robot,
2004 &objectives_test,
2005 0,
2006 &a_ra0,
2007 (0.0, 0.0),
2008 (0.0, 0.0),
2009 )
2010 .build_with_linearization()?;
2011 objective_value_copp3_opt(&copp3_problem, (&a_case4, &b_case4, num_stationary))
2012 };
2013 if obj_case4[1] < obj_case1[1] - 1E-3 || obj_case4[3] - 1E-3 > obj_case1[3] {
2014 crate::verbosity_log!(
2015 crate::diag::Verbosity::Summary,
2016 "omega = {omega:?}\nphi = {phi:?}"
2017 );
2018 crate::verbosity_log!(
2019 crate::diag::Verbosity::Summary,
2020 "Case 1: obj_time = {}, obj_thermal_energy = {}, obj_linear = {}",
2021 obj_case1[0],
2022 obj_case1[1],
2023 obj_case1[3]
2024 );
2025 crate::verbosity_log!(
2026 crate::diag::Verbosity::Summary,
2027 "Case 4: obj_time = {}, obj_thermal_energy = {}, obj_linear = {}",
2028 obj_case4[0],
2029 obj_case4[1],
2030 obj_case4[3]
2031 );
2032 crate::verbosity_log!(
2033 crate::diag::Verbosity::Summary,
2034 "Interesting... Cases 1 and 4"
2035 );
2036 }
2037 if obj_case4[2] < obj_case2[2] - 1E-3 || obj_case4[3] - 1E-3 > obj_case2[3] {
2038 crate::verbosity_log!(
2039 crate::diag::Verbosity::Summary,
2040 "omega = {omega:?}\nphi = {phi:?}"
2041 );
2042 crate::verbosity_log!(
2043 crate::diag::Verbosity::Summary,
2044 "Case 2: obj_time = {}, obj_total_variation_torque = {}, obj_linear = {}",
2045 obj_case2[0],
2046 obj_case2[2],
2047 obj_case2[3]
2048 );
2049 crate::verbosity_log!(
2050 crate::diag::Verbosity::Summary,
2051 "Case 4: obj_time = {}, obj_total_variation_torque = {}, obj_linear = {}",
2052 obj_case4[0],
2053 obj_case4[2],
2054 obj_case4[3]
2055 );
2056 crate::verbosity_log!(
2057 crate::diag::Verbosity::Summary,
2058 "Interesting... Cases 2 and 4"
2059 );
2060 }
2061
2062 succeed += 1;
2063
2064 if flag_print_step {
2065 crate::verbosity_log!(
2066 crate::diag::Verbosity::Summary,
2067 "Exp #{}:\n Case 0: tc={:.3}ms, obj={:?}\n Case 1: tc={:.3}ms, obj={:?}\n Case 2: tc={:.3}ms, obj={:?}\n Case 3: tc={:.3}ms, obj={:?}\n Case 4: tc={:.3}ms, obj={:?}",
2068 i_exp + 1,
2069 tc_copp3_case0,
2070 obj_case0,
2071 tc_copp3_case1,
2072 obj_case1,
2073 tc_copp3_case2,
2074 obj_case2,
2075 tc_copp3_case3,
2076 obj_case3,
2077 tc_copp3_case4,
2078 obj_case4
2079 );
2080 }
2081
2082 tc_sum_case0 += tc_copp3_case0;
2083 tc_sum_case1 += tc_copp3_case1;
2084 tc_sum_case2 += tc_copp3_case2;
2085 tc_sum_case3 += tc_copp3_case3;
2086 tc_sum_case4 += tc_copp3_case4;
2087 for i in 0..obj_case0.len() {
2088 obj_sum_case0[i] += obj_case0[i];
2089 obj_sum_case1[i] += obj_case1[i];
2090 obj_sum_case2[i] += obj_case2[i];
2091 obj_sum_case3[i] += obj_case3[i];
2092 obj_sum_case4[i] += obj_case4[i];
2093 }
2094 }
2095
2096 for i in 0..4 {
2097 obj_sum_case0[i] /= n_exp as f64;
2098 obj_sum_case1[i] /= n_exp as f64;
2099 obj_sum_case2[i] /= n_exp as f64;
2100 obj_sum_case3[i] /= n_exp as f64;
2101 obj_sum_case4[i] /= n_exp as f64;
2102 }
2103
2104 crate::verbosity_log!(
2105 crate::diag::Verbosity::Summary,
2106 "Average {n_exp} experiments (fail {}):\n Case 0: tc={:.3}ms, obj={obj_sum_case0:?}\n Case 1: tc={:.3}ms, obj={obj_sum_case1:?}\n Case 2: tc={:.3}ms, obj={obj_sum_case2:?}\n Case 3: tc={:.3}ms, obj={obj_sum_case3:?}\n Case 4: tc={:.3}ms, obj={obj_sum_case4:?}",
2107 n_exp - succeed,
2108 tc_sum_case0 / succeed as f64,
2109 tc_sum_case1 / succeed as f64,
2110 tc_sum_case2 / succeed as f64,
2111 tc_sum_case3 / succeed as f64,
2112 tc_sum_case4 / succeed as f64
2113 );
2114
2115 Ok(())
2116 }
2117}